#ifndef __STRING_UTIL_H
#define __STRING_UTIL_H

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string>
#include <stack>
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>

#include "baseutil.h"

using namespace std;

class String {
private:
	boost::shared_array<char> arr;
	size_t capcity_;
	size_t count;
private:
	//clone self when writing
	void maybeCloneSelf(size_t increase = 0);
public:
	String(const char* s, size_t len);
	String(const char* s);
	String(const string& s);
	String(const String& other);
	explicit String(size_t capcity);
	String();
	virtual ~String();
public:
	String& operator=(const String& other);

	inline char operator[](int index) const {
		assert(index >=0);
		return get(index);
	}
	inline char& operator[](int index) {
		assert( index >= 0);
		return getRef(index);
	}
public:
	void copyStr(const char* s, size_t len);
	//will delete[] s, when String Objects destructor
	void manageStr(char* s, size_t len);

	inline void clear(){
		capcity_ = count = 0;
		arr.reset(0);
	}
	inline size_t getCapcity() {
		return capcity_;
	}
	inline String clone() {
		String s(arr.get(), length());
		return s;
	}
	inline const char* getBufferRead() const {
		return arr.get();
	}
	inline char* getBufferWrite() {
		maybeCloneSelf();
		return arr.get();
	}
	int inline length() const {
		return count;
	}
	inline int capcity() const {
		return capcity_;
	}
	inline bool empty() const {
		return count == 0;
	}
	inline char get(size_t index) const {
		assert( index < count);
		return arr[index];
	}
	inline char& getRef(size_t index) {
		assert( index <= count);
		if (index == count) {
			append(0);
		} else {
			maybeCloneSelf();
		}
		return arr[index];
	}
	inline void set(size_t index, char ch) {
		assert( index <= count);
		if (index == count) {
			append(ch);
		} else {
			maybeCloneSelf();
			arr[index] = ch;
		}
	}
	inline void setLength(size_t len) {
		assert( len <= capcity_);
		count = len;
	}
	inline void append(char ch) {
		replace(count, 0, &ch, 1);
	}
	inline void append(const char* s, size_t len) {
		replace(count, 0, s, len);
	}
	inline void append(const String& s) {
		replace(count, 0, s);
	}

	inline void insert(size_t index, char ch) {
		replace(index, 0, &ch, 1);
	}
	inline void insert(size_t index, const char* s, size_t len) {
		replace(index, 0, s, len);
	}
	inline void insert(size_t index, const String& s) {
		replace(index, 0, s);
	}
	inline void remove(size_t index) {
		replace(index, 1, 0, 0);
	}
	inline void remove(size_t index, size_t size) {
		replace(index, size, 0, 0);
	}
	inline void replace(size_t from, size_t length, const String& s) {
		replace(from, length, s.arr.get(), s.length());
	}
	inline void replace(size_t from, size_t length, const char* values) {
		size_t len = 0;
		if (values) {
			len = strlen(values);
		}
		replace(from, length, values, len);
	}
	void replace(size_t from, size_t length, const char* values, size_t valuesCount);
	String subString(size_t from, size_t length);
	//[from, length())
	String subString(size_t from);

	bool equals(const char* other);
	inline bool equals(const String& other) {
		return equals(other.arr.get(), other.count);
	}
	inline bool equals(const char* other, size_t len) {
		if (length() == len) {
			return rangeEquals(0, other, len);
		}
		return false;
	}

	bool startWith(const char* other);
	bool startWith(const String& other) {
		return rangeEquals(0, other.getBufferRead(), other.length());
	}
	inline bool startWith(const char* other, size_t len) {
		return rangeEquals(0, other, len);
	}

	inline bool endWith(const char* other) {
		return endWith(other, other ? strlen(other) : 0);
	}
	inline bool endWith(const String& other) {
		return endWith(other.getBufferRead(), other.length());
	}
	inline bool endWith(const char* other, size_t len) {
		if (length() >= len) {
			return rangeEquals(length() - len, other, len);
		}
		return false;
	}

	inline string toStdString(){
		return string(getBufferRead(), length());
	}
private:
	void insertCheck(int delta);

	//[from, from+len) eq [other, other+len);
	bool rangeEquals(size_t from, const char* other, size_t len);

};

ostream& operator <<(ostream& slog, const String& s);

#endif  //__STRING_UTIL_H
